use i_triangle::i_overlay::i_float::f64_point::F64Point;
use serde::Serialize;

#[derive(Serialize, Clone)]
struct Edge {
    start: F64Point,
    end: F64Point,
}

const EPSILON: f64 = 1e-6;
fn points_equal(p1:&F64Point, p2:&F64Point)->bool{
    (p1.x - p2.x).abs() < EPSILON && (p1.y - p2.y).abs() < EPSILON
}

impl PartialEq for Edge {
    fn eq(&self, other: &Self) -> bool {
        (points_equal(&self.start, &other.start) && points_equal(&self.end, &other.end)) ||
        (points_equal(&self.start, &other.end) && points_equal(&self.end, &other.start))
    }
}

#[derive(Serialize, Clone)]
pub struct Polygon {
    edges: Vec<Edge>,
    centroid: F64Point,
    index: usize,
}

impl Polygon {
    pub fn with_points(points: &Vec<F64Point>, index:usize)->Self{
        let mut edges = Vec::new();
        let n = points.len();

        if n > 1 {
            for i in 0..n {
                let start = points[i].clone();
                let end = points[(i + 1) % n].clone();
                edges.push(Edge { start, end });
            }
        }

        let centroid = Polygon::calculate_centroid(points);

        Self { edges, centroid, index }
    }

    fn calculate_centroid(points: &[F64Point]) -> F64Point {
        let (sum_x, sum_y) = points.iter().fold((0.0, 0.0), |(acc_x, acc_y), point| {
            (acc_x + point.x, acc_y + point.y)
        });
        let n = points.len() as f64;
        F64Point {
            x: sum_x / n,
            y: sum_y / n,
        }
    }
}

impl Polygon {
    // 判断两个 Polygon 是否有相同的邻边
    fn has_common_edge(&self, other: &Polygon) -> bool {
        for edge in &self.edges {
            if other.edges.contains(edge) {
                return true;
            }
        }
        false
    }
}

#[derive(Serialize, Clone)]
pub struct Polygons {
    polygons: Vec<Polygon>,
}

impl Polygons {
    pub fn new() -> Self {
        Self {
            polygons: Vec::new(),
        }
    }

    pub fn add_polygon(&mut self, polygon: Polygon) {
        self.polygons.push(polygon);
    }

    pub fn generate_neighbors(&self) -> Vec<Vec<usize>> {
        let mut neighbors = vec![Vec::new(); self.polygons.len()];

        // 重新计算邻边
        let n = self.polygons.len();
        for i in 0..n {
            for j in (i + 1)..n {
                if self.polygons[i].has_common_edge(&self.polygons[j]) {
                    neighbors[i].push(j);
                    neighbors[j].push(i);
                }
            }
        }

        neighbors
    }
}

#[derive(Serialize, Clone, Debug)]
pub struct NavMesh {
    points: Vec<F64Point>,
    polygons: Vec<NavPolygon>,
    neighbors: Vec<Vec<usize>>, 
    merged_polygon_indexs: Vec<Vec<Vec<usize>>>
}

#[derive(Serialize, Clone, Debug)]
pub struct NavPolygon {
    point_indices: Vec<usize>,
    centroid: F64Point,
    polygon_index: usize,
}

impl NavMesh {
    pub fn new(polygons: Polygons, merged_polygon_datas:Vec<Vec<Vec<F64Point>>>) -> Self {
        let mut all_points = Vec::new();
        let mut polygon_indices = Vec::new();

        for polygon in &polygons.polygons {
            let mut indices = Vec::new();
            for edge in &polygon.edges {
                let start_index = all_points.iter().position(|p:&F64Point| p.x == edge.start.x && p.y == edge.start.y).unwrap_or_else(|| {
                    all_points.push(edge.start.clone());
                    all_points.len() - 1
                });
                indices.push(start_index);
            }
            polygon_indices.push(NavPolygon {
                point_indices: indices,
                centroid: polygon.centroid.clone(),
                polygon_index: polygon.index,
            });
        }

        let neighbors = polygons.generate_neighbors();

        let mut merged_polygon_indexs = Vec::new();

        for merged_polygons in merged_polygon_datas {
            let mut merged_polygon_group = Vec::new();
            for polygon in merged_polygons {
                let mut indices = Vec::new();
                for point in polygon {
                    let point_index = all_points.iter().position(|p: &F64Point| p.x == point.x && p.y == point.y).unwrap_or_else(|| {
                        all_points.push(point.clone());
                        all_points.len() - 1
                    });
                    indices.push(point_index);
                }
                merged_polygon_group.push(indices);
            }
            merged_polygon_indexs.push(merged_polygon_group);
        }

        Self {
            points: all_points,
            polygons: polygon_indices,
            neighbors,
            merged_polygon_indexs
        }
    }
}
